package model;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class Itinerary {

    private final Graph graph;
    private int tab[];
    private int sum;
    private Set<Edge> edges;

    public Itinerary(int tab[], Graph graph) {
        this.graph = graph;
        this.tab = tab;
        update();
    }

    // funkcja aktualizujaca edges i sum na podstawie tab, 
    // powinna byc wywolywana po kazdej zmianie tab
    private void update() {
        int sum = 0;
        Set<Edge> edges = new HashSet<>(tab.length);

        for (int i = 0; i < tab.length - 1; i++) {
            sum += graph.get(tab[i], tab[i + 1]);
            edges.add(new Edge(tab[i], tab[i + 1]));
        }
        sum += graph.get(tab[0], tab[tab.length - 1]);
        edges.add(new Edge(tab[tab.length - 1], tab[0]));

        this.sum = sum;
        this.edges = edges;
    }

    public int get(int index) {
        return tab[index];
    }

    public int[] getTab() {
        return tab;
    }

    public int getSum() {
        return sum;
    }

    // called only by Solver.class or subclasses
    public void set(int tab[]) {
        this.tab = tab;
        update();
    }

    public Set<Edge> intersection(Itinerary itin) {
        Set<Edge> commonEdges = new HashSet<>(edges.size());
        if (tab.length < 2)
            return null;
        for (Edge edge1 : edges)
            for (Edge edge2 : itin.edges)
                if (edge1.equals(edge2))
                    commonEdges.add(edge2);
        return commonEdges;
    }

    public int similarity(Itinerary itin) {
        return intersection(itin).size();
    }

    public void randomMove() { // ?????
        Random random = new Random();
        int tmpToSwap;
        int vertexFirstToSwap = random.nextInt(tab.length);
        int vertexSecondToSwap;
        do {
            vertexSecondToSwap = random.nextInt(tab.length);
        } while (vertexFirstToSwap == vertexSecondToSwap);

        tmpToSwap = tab[vertexFirstToSwap];
        tab[vertexFirstToSwap] = tab[vertexSecondToSwap];
        tab[vertexSecondToSwap] = tmpToSwap;
        update();
    }

    private void swapFap(int firstToSwap, int secondToSwap) {
        int tmpToSwap;
        if (firstToSwap < secondToSwap) {
            tmpToSwap = tab[secondToSwap];
            for (int i = secondToSwap - 1; i > firstToSwap; i--)
                tab[i + 1] = tab[i];
            tab[firstToSwap + 1] = tmpToSwap;
        } else {
            tmpToSwap = tab[firstToSwap];
            for (int i = firstToSwap - 1; i >= secondToSwap; i--)
                tab[i + 1] = tab[i];
            tab[secondToSwap] = tmpToSwap;
        }
    }

    // called only when 2 inits got different set of edges
    public void move(Itinerary itin) {
        Set<Edge> commonEdges = intersection(itin);
        boolean contains = true;
        int i = 0;
        int secondVertex = 0, firstToSwap = 0;
        while (contains) {
            contains = false;
            for (Edge edge : commonEdges)
                if (tab[i] == edge.getA() || tab[i] == edge.getB())
                    contains = true;
            if (!contains)
                break;
            i++;
        }
        int firstVertex = tab[i];
        for (Edge edge : itin.edges) {
            if (edge.getA() == firstVertex) {
                secondVertex = edge.getB();
                break;
            }
        }
        int secondToSwap = 0;

        for (int j = 0; j < tab.length; j++) {
            if (tab[j] == firstVertex)
                firstToSwap = j;
            if (tab[j] == secondVertex) {
                secondToSwap = j;
            }
        }
        swapFap(firstToSwap, secondToSwap);
        update();
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        if (tab.length > 0) {
            for (int i = 0; i < tab.length - 1; i++) {
                sb.append(tab[i] + ", ");
            }
            sb.append(tab[tab.length - 1]);
        }
        sb.append("]");
        return sb.toString();
    }

    public String showSolution() {
        return "Solution: {" + toString() + ", " + sum + "}";
    }
}
